/************************************************************************

								Heroes IV
					  Copyright 2000, The 3DO Company

	------------------------------------------------------------------
  						       report_combat_results.cpp

	$Header: /game/report_combat_results.cpp $

	$NoKeywords: $

 ************************************************************************/

// kill the 255+ character identifier warning.  Templates tend to exceed that easily.
#include "precompiled.h"
#pragma warning( disable: 4786 )

#include "report_combat_results.h"

#include "abstract_grail_data_source.h"
#include "adv_artifact_pile.h"
#include "adventure_map.h"
#include "army_list.h"
#include "army.h"
#include "artifact.h"
#include "basic_dialog.h"
#include "combat_result.h"
#include "counted_ptr.h"
#include "creature.h"
#include "creature_ability.h"
#include "creature_array.h"
#include "creature_traits.h"
#include "creature_type.h"
#include "dialog_booty.h"
#include "dialog_combat_results.h"
#include "dialog_full_army.h"
#include "dialog_necromancy.h"
#include "enum_operations.h"
#include "external_string.h"
#include "footprint.h"
#include "format_string.h"
#include "hero.h"
#include "music.h"
#include "player.h"
#include "random.h"
#include "random_monster_experience.h"
#include "replace_keywords.h"
#include "resurrect_heroes.h"
#include "simple_dialog.h"
#include "skill.h"
#include "skill_properties.h"
#include "sound.h"
#include "sound_cache.h"
#include "town_type.h"
#include "town.h"
#include "town_list.h"

// -----------------------------------------------------------------------
// Distribute any remaining booty which the player (if there is a human 
// player) didn't distribute amoung the winning creature stack(s)
// -----------------------------------------------------------------------
void auto_distribute_booty( t_creature_array & winners, t_artifact_list & booty )
{
	if ( booty.empty() )
		return;

	// Find a living hero, dead hero, or creature to add remaining booty to
	t_creature_stack * target_stack = NULL;
	int index = 0;

	while ( index < t_creature_array::k_size && target_stack == NULL )
	{
		if ( winners[index].get_hero() && !winners[index].get_hero()->is_dead() )
		{
			target_stack = &winners[index];
		}
		else
		{
			index++;
		}
	}

	// Try dead heroes if no live ones
	index = 0;

	while ( index < t_creature_array::k_size && target_stack == NULL )
	{
		if ( winners[index].get_hero() )
		{
			target_stack = &winners[index];
		}
		else
		{
			index++;
		}
	}

	// Try creatures if no heroes
	index = 0;
	while ( index < t_creature_array::k_size && target_stack == NULL )
	{
		if ( dynamic_cast< t_stack_with_backpack * >( &winners[index] ) )
		{
			target_stack = &winners[index];
		}
		else
		{
			index++;
		}
	}

	assert( target_stack );

	if ( target_stack )
	{
		index = 0;
		while ( index < booty.size() )
		{
			target_stack->add( booty[index] );
			index++;
		}

		booty.clear();
	}
}


static t_external_string const k_post_combat_booty_title( "post_combat_booty_title" );
static t_external_string const k_post_combat_booty_text_singular( "post_combat_booty_text_singular" );
static t_external_string const k_post_combat_booty_text_plural( "post_combat_booty_text_plural" );

// -----------------------------------------------------------------------
// Distribute the booty (captured artifacts) amoung the winning army
// -----------------------------------------------------------------------
static void distribute_booty( t_window * parent, 
							  t_creature_array * victor, t_player* victorious_player, 
							  t_creature_array * loser, t_player* losing_player, 
							  bool victor_is_real_army, 
							  t_adv_map_point combat_point, t_adv_map_point alternate_point,
							  t_artifact_list & booty,
							  t_adventure_map * map )
{
	if ( booty.empty() )
		return;

	if ( victor && victor_is_real_army )
	{
		if ( victorious_player && !victorious_player->is_computer() )
		{
			// Give player chance to see & distribute artifacts
			std::string text;
			if ( booty.size() == 1 )
			{
				text = k_post_combat_booty_text_singular;
			}
			else
			{
				text = k_post_combat_booty_text_plural;
			}

			t_counted_ptr< t_dialog_booty > booty_dialog = new t_dialog_booty( parent, *victor, booty, k_post_combat_booty_title, text );

			booty_dialog->run_modal();
		}
		else
		{
			auto_distribute_booty( *victor, booty );
		}
	}
	else
	{
		// No winner, or winner is a scripted army. All artifacts drop into a new treasure pile
		t_adv_artifact_pile_ptr pile;

		// Find a good place for this pile
		t_adv_map_point point;

		if ( combat_point == alternate_point )
		{
			point = combat_point;

			// To look for existing pile
			t_adv_artifact_pile::is_good_creation_point( point, pile, map );
		}
		else
		{
			if ( t_adv_artifact_pile::is_good_creation_point( combat_point, pile, map ) )
				point = combat_point;
			else if ( t_adv_artifact_pile::is_good_creation_point( alternate_point, pile, map ) )
				point = alternate_point;
			else
				point = combat_point; // Have to put it somewhere!
		}

		if ( pile == NULL )
		{
			pile = new t_adv_artifact_pile( map->is_ocean( point ) );

			pile->place( *map, point );
		}

		pile->add_artifacts( booty, victorious_player, losing_player );
	}
}

			
// -----------------------------------------------------------------------
// handle post combat resurrection
// -----------------------------------------------------------------------
t_hero* do_resurrection( t_creature_array&			victor,
						 t_creature_array const&	victor_losses,
						 t_creature_array&			resurrected_creatures,
						 bool						apply_results )
{
	t_hero*			 priest = victor.get_most_powerful_skill( k_skill_resurrection );
	int				 percentage;
	int				 amount;
	int				 i;

	if (priest == 0)
		return 0;

	percentage = priest->get_skill_power( k_skill_resurrection );
	for (i = 0; i < t_creature_array::k_size; i++)
	{
		if (victor_losses[i].get_creature() == 0)
			continue;

		if (victor_losses[i].has_ability( k_ability_undead )
			|| victor_losses[i].has_ability( k_ability_mechanical )
			|| victor_losses[i].has_ability( k_ability_elemental ))
			continue;

		amount = victor_losses[i].get_number() * percentage / 100;
		if (amount < 1)
			continue;

		resurrected_creatures.add( victor_losses[i].get_creature_type(), amount );
		
		if (apply_results)
			victor.add( victor_losses[i].get_creature_type(), amount );
	}
	return priest;
}


// -----------------------------------------------------------------------
// handle post combat summoning
// -----------------------------------------------------------------------
void do_necromancy( t_creature_array  &		victor,
						   t_player*				victorious_player,
						   t_creature_array const &	vanquished_losses,
						   t_hero**					necromancer,
						   t_creature_array &		new_necro_creatures )
{
	int					percentage;
	int					amount;
	int					total = 0;
	int					total_creatures = 0;
	t_creature const*	creature;
	int					i;
	int					limit;

	*necromancer = victor.get_most_powerful_skill( k_skill_necromancy );
	if (*necromancer == 0)
		return;

	static int const		k_limit[] = { 40, 40, 80, 120, 160, 200 };
	static t_creature_type	k_necromancy_creature[] = 
	{ k_skeleton, k_skeleton, k_skeleton, k_ghost, k_ghost, k_vampire };
	t_skill_mastery			mastery = (*necromancer)->get_skill( k_skill_necromancy );

	percentage = (*necromancer)->get_skill_power( k_skill_necromancy );
	limit = k_limit[ mastery + 1];
	limit += limit * ((*necromancer)->get_level() - 1) / 10;

	if (victorious_player != NULL)
	{
		percentage += victorious_player->get_necromancy_amplifier_bonus();
	}

	if ( victor.get_grail_data().gets_global_grail_effects( k_town_death ) )
	{
		// Infernal Effigy increases necromancy limit & power
		limit += limit / 2;
		percentage += 10;
	}


	if (percentage > 100)
		percentage = 100;

	for (i = 0; i < t_creature_array::k_size; i++)
	{
		creature = vanquished_losses[i].get_creature();
		if (creature == 0)
			continue;
		if (creature->has_ability( k_ability_undead )
			|| creature->has_ability( k_ability_mechanical )
			|| creature->has_ability( k_ability_elemental ))
			continue;
		amount = creature->get_experience_value() * percentage / 100;
		if (amount < 1)
			continue;

		total_creatures += creature->get_number();
		total += amount;
	}
	if (total > limit)
		total = limit;
	if (total <= 0)
		return;

	// choose something to summon
	for (i = mastery + 1; i >= 0; --i)
	{
//		if (victor.can_add( k_necromancy_creature[i] )
//			&& get_traits( k_necromancy_creature[i]).experience <= total)
		if (get_traits( k_necromancy_creature[i]).experience <= total)
			break;
	}
	if (i < 0)
		return;

	t_creature_type      creature_type = k_necromancy_creature[i];
	int					 experience = get_traits( creature_type ).experience;

	amount = (total + experience / 2) / experience;
	if (amount > total_creatures)
		amount = total_creatures;
	if (amount <= 0)
		return;

	new_necro_creatures.add( creature_type, amount );
}


// -----------------------------------------------------------------------
// handle post combat experience
// -----------------------------------------------------------------------
static void post_combat_victor( t_creature_array& victor, 
							    t_player        * victorious_player,
							    t_creature_array const& victor_losses,
							    t_creature_array const& vanquished_losses,
								t_town			* town,
							    t_window		* parent )
{
	int	experience = vanquished_losses.get_experience_value();

	// do resurrection
	t_creature_array     resurrected_creatures;
	t_creature_array	 necro_creatures_array;
//	t_creature_stack_ptr necromancy_creatures;
	t_hero*				 priest = 0;
	t_hero*				 summoner = 0;
	t_hero*				 necromancer = 0;

	priest = do_resurrection( victor, victor_losses, resurrected_creatures );
	do_necromancy( victor, victorious_player, vanquished_losses, 
				  &necromancer, necro_creatures_array );
	
	if (parent == 0 || !victorious_player || victorious_player->is_computer())
	{
		// Drop in the creatures -- just abandon them if there's no room in the army
		merge( victor, necro_creatures_array );
	}
	else 
	{
		if (!resurrected_creatures.empty() || !necro_creatures_array.empty() )
		{
			t_counted_ptr<t_dialog_necromancy> dialog;

			dialog = new t_dialog_necromancy( parent );
			if (!resurrected_creatures.empty())
				dialog->set_resurrection( priest, resurrected_creatures );
			if ( !necro_creatures_array.empty() )
				dialog->set_necromancy( necromancer, &necro_creatures_array[0] );
			dialog->open();
			dialog->run_modal();

			merge( victor, necro_creatures_array );

			// If there are creatures left over, we couldn't add them all.
			// Throw up the full army dialog
			if( !necro_creatures_array.empty() )
			{
				t_counted_ptr<t_dialog_full_army> dialog_ptr;

				dialog_ptr = new t_dialog_full_army( parent );
				dialog_ptr->init_dialog( victor, necro_creatures_array );
				dialog_ptr->run_modal();
			}
		}
	}

	if (victorious_player != 0 && !victorious_player->is_computer())
	{
		t_adventure_map& map = *victor.get_map();

		experience = experience / get_monster_difficulty( map.get_player_difficulty() ) + 0.5;
	}

	victor.add_experience( experience, parent );

	if ( town )
	{
		victor.visit_town( town );

		// Apply bonuses but only if we don't actually need to display a message for them
		t_army * army = victor.get_army();

		bool victor_is_town = ( &town->get_garrison() == &victor );

		town->check_bonus_buildings( army, NULL, victor_is_town );
	}
}


// -----------------------------------------------------------------------
// kill defeated heroes
// -----------------------------------------------------------------------
static void kill_heroes( t_creature_array& army )
{
	int     i;
	t_hero* hero;

	for (i = 0; i < t_creature_array::k_size; i++)
	{
		hero = army[i].get_hero();
		if (hero != 0)
			hero->set_dead( true );
		else
		{
			t_creature* creature = army[i].get_creature();

			// See if there is a creature or if there's nothing period
			if (creature)
			{
				// Kill all the creatures in this slot
				army.clear(i);
			}
		}
	}
}


// Add hero's/creatures artifacts to the captured booty
static void strip_of_artifacts( t_creature_stack* stack, t_artifact_list & booty )
{
	while ( stack->get_backpack_count() )
	{
		booty.push_back( stack->remove_backpack( 0 ) );
	}

	t_hero * hero = stack->get_hero();
	
	if ( hero )
	{
		t_artifact_slot slot;

		for ( slot = t_artifact_slot(0); slot < k_artifact_slot_hero_count; enum_incr( slot ) )
		{
			t_artifact artifact = hero->remove( slot );

			if ( artifact.get_icon() != k_artifact_none )
			{
				booty.push_back( artifact );
			}
		}
	}
}

static t_external_string const k_prisoner_text( "combat_results_prisoner.misc" );
/*
** Check for prisoners and remove heroes, if necessary.
*/
static void check_for_prisoners( t_creature_array&			winning_army, 
								 t_player*					winner_player, 
						         t_creature_array&			loser_array,
					             t_player*					loser_player,				
    					         t_adv_map_point const&     location,
								 t_town*					town_ptr,
								 t_artifact_list &			booty)
{
	int  i;
	t_hero * hero_ptr;

	// Strip losing heroes & creatures of artifacts, regardless of what else happens to them
	for (i = 0; i < t_creature_array::k_size; ++i)
	{
		t_stack_with_backpack * stack = dynamic_cast< t_stack_with_backpack * >( &loser_array[i] );
		if ( stack )
		{
			strip_of_artifacts( stack, booty );
		}
	}


    if ( loser_player == 0 )
    {
		// clear out any heroes, neutrals don't leave prisoners or bodies.
		for (i = 0; i < t_creature_array::k_size; ++i)
		{
			hero_ptr = loser_array[i].get_hero();
			if ( hero_ptr != 0)
			{
				loser_array.clear( i );
			}
		}
        return;
    }

	int  owner_number;

	// if attacker can't take town to use as a prison, mark town as null.
	if (town_ptr != 0 && winning_army.get_living_hero_count() == 0
		&& town_ptr->get_owner_number() != winning_army.get_owner_number())
		town_ptr = 0;
	if (town_ptr == 0)
	{
		if (winner_player == 0)
			return;

		t_town_list*    winner_town_list = winner_player->get_towns();
		if ( winner_town_list->size() == 0)
		{
			return;
		}

		// Find closet town.
		double                  shortest_distance = 99999999;
		int                     town_index = 0;

		for (i=0; i<winner_town_list->size(); i++)
		{
			town_ptr = (*winner_town_list)[i];
			double town_distance = distance ( location - town_ptr->get_position() );
			if (town_distance <= shortest_distance)
			{
				shortest_distance = town_distance;
				town_index = i;
			}
		}

		// Town to put prisoners in.
		town_ptr = (*winner_town_list)[town_index];
	}

    // Get loser owner number.
    t_adventure_map* map_ptr = town_ptr->get_map();
    t_player_color color = loser_player->get_color();
    owner_number = map_ptr->get_player_number( color );

    // Put all losing heroes in town.
	std::string             result_text;
	std::string             hero_names_text;
	bool					first = true;
	bool					any_heroes = false;

    for (i = 0; i<t_creature_array::k_size; i++)
    {
		hero_ptr = loser_array[i].get_hero();
        if (hero_ptr == 0)
		    continue;

		any_heroes = true;

		hero_ptr->set_dead( false );
        hero_ptr->set_wounds( 0 );

	    t_town_prisoner prisoner;
        prisoner.hero = hero_ptr;
        prisoner.owner_number = owner_number;

        // Put hero in prison.
        town_ptr->add_prisoner ( &prisoner );

		if (!first)
			hero_names_text = hero_names_text + ", ";
		hero_names_text = hero_names_text + hero_ptr->get_name();

        // Remove hero.
        loser_array.clear( i );
		first = false;
    }

    if ( !any_heroes )
		return;

	if ((winner_player != 0 && !winner_player->is_computer()) || (!loser_player->is_computer()))
    {
       t_counted_ptr<t_basic_dialog>   result_dialog_ptr;
        
        result_text = replace_keywords( k_prisoner_text, "%heroes", hero_names_text,
                                    "%town", town_ptr->get_name() );
    
        result_dialog_ptr = new t_basic_dialog;
        result_dialog_ptr->set_text( result_text );
        result_dialog_ptr->add_ok_button();
        result_dialog_ptr->open();
        result_dialog_ptr->run_modal();
    }
}


void strip_artifacts( t_creature_array& army, t_artifact_list& booty )
{
	int i;

	for (i =0 ; i < t_creature_array::k_size; ++i)
	{
		t_creature_stack& stack = army[i];

		if (stack.get_number() == 0)
			continue;
		strip_of_artifacts( &stack, booty );
	}
}

// -----------------------------------------------------------------------
// Grab all artifacts off of both armies and add them to booty
// -----------------------------------------------------------------------
static void grab_artifacts( t_creature_array ** armies, t_artifact_list & booty )
{
	strip_artifacts( *armies[0], booty );
	strip_artifacts( *armies[1], booty );
}

// -----------------------------------------------------------------------
// handle creating and moving an army to the nearest owned town.
// what we are doing here, is taking ALL creatures from the source army,
// creating a brand new army near the selected town, and dumping the
// original army when we are done
// -----------------------------------------------------------------------
static void transfer_army_to_nearest_town( t_creature_array*		creatures,
										   t_player*				player,
    									   t_adv_map_point const&	location,
										   t_town const*			excluded_town,
										   t_army_ptr &				new_army )
{
	assert( creatures );

    if (!player || creatures->empty())
		return;

	// Find the nearest town with an open space
	// (Note: the location we've been passed was originally extracted from a town, so we assume
	//  we'll be able to go back and find the town it's associated with.)
	t_town *           destination_town;
	t_adv_map_point		    destination_location;
    t_adventure_map*		map_ptr = creatures->get_map();
	destination_town = ::find_nearest_town( map_ptr, player, location, 
											destination_location, creatures, excluded_town,
											0, 0, true );
	assert( destination_town );	// Why couldn't we find the town anymore?

	// Transfer the creatures, then set some other requires properties
	// NOT including the boat type since we're moving onto land 
	// (because find_nearest_town always finds land spots).
	new_army = new t_army( creatures );
	t_player_color color = player->get_color();
	int owner_number = map_ptr->get_player_number( color );
	new_army->set_owner( owner_number );

	new_army->place( *map_ptr, destination_location );

	new_army->visit_town( destination_town );

	// Give bonuses & teach spells, but don't display messages
	destination_town->check_bonus_buildings( new_army, NULL );
}

// -----------------------------------------------------------------------
// handle an army's retreat
// -----------------------------------------------------------------------
static void retreat_army( t_creature_array&			loser_creatures,
					      t_player*					loser_player,
    					  t_adv_map_point const&    location,
						  t_town const*				town,
						  t_army_ptr &				new_army  )
{
	// Kill all creatures, hurt all heros
	for (int i = 0; i<t_creature_array::k_size; i++)
	{
		t_hero* hero_ptr;

		hero_ptr = loser_creatures[i].get_hero();
		// Make sure we don't remove the heroes from the army
		if (hero_ptr == 0)
		{
			t_creature* creature = loser_creatures[i].get_creature();

			// See if there is a creature or if there's nothing period
			if (creature)
			{
				// Kill all the creatures in this slot
				loser_creatures.clear(i);
			}
		} 
		else
		{
			// It's a hero, hurt him bad, if he's not already dead
			if( !hero_ptr->is_dead() )
				hero_ptr->set_wounds( hero_ptr->get_hit_points() - 1 );
		}
	}

	// Leave the creatures with very little movement, just enough to get into town. 
	// This cuts down on repeated hero-only attacks on near-town tough armies
	// (attack with heroes, do some damage, retreat; attack with heroes, do some
	// damage, retreat. Without the next line, you could pull this 20 times in a row)
	loser_creatures.expend_most_movement( 310 );

	transfer_army_to_nearest_town( &loser_creatures, loser_player, location, town, new_army );
}

// -----------------------------------------------------------------------
// handle an army's surrender
// -----------------------------------------------------------------------
static void surrender_army( t_creature_array&		loser_creatures,
							t_player*				loser_player,
    						t_adv_map_point const&  location,
							t_town const*			town,
							bool					charge_for_surrender,
							t_army_ptr &			new_army  )
{
	transfer_army_to_nearest_town( &loser_creatures, loser_player, location, town, new_army );
}

// -----------------------------------------------------------------------
// report results of combat
// -----------------------------------------------------------------------
void report_combat_results( t_creature_array**		armies,
						    t_player**				players,
							t_creature_array*		losses,
							t_combat_result			result,
							t_window*				window,
							t_town const*			town,
							t_adv_map_point const&	location,
							t_creature_stack_ptr ( & leaders )[ 2 ] )
{
	t_counted_ptr<t_combat_results_dialog> combat_results_dialog;

	t_sound_cache music = get_music_playing();
	stop_music();

	combat_results_dialog = new t_combat_results_dialog( window, armies, players, 
														 losses, result, town, 
														 location, leaders );

	combat_results_dialog->run_modal();

	play_music( music );
}


static t_external_string const k_text_resurrect_heroes_post_retreat( "resurrect_heroes_post_retreat.misc" );
extern t_external_string const k_text_resurrect_heroes;

// -----------------------------------------------------------------------
// Post process the results of combat.  Generates some UI and adjusts
// the contents of the armies depending on various skills and states of
// the heros, spells in effect, etc.
// -----------------------------------------------------------------------
void post_process_combat_results( t_creature_array**		armies,
								  t_player**				players,
								  t_creature_array*			losses,
								  t_combat_result			result,
								  t_adv_map_point const&	location,
								  t_window*					window,
								  t_town const*				town )
{
	t_adventure_map *map = armies[0]->get_map();

	if ( !map )
		map = armies[1]->get_map();

	if ( !map && town )
		map = town->get_map();

	switch (result)
	{
		// Check if we can take prisoners and kill defeated heroes
		case k_result_defender_lost:
			kill_heroes( *armies[k_side_defender] );
			break;

		case k_result_attacker_lost:
			kill_heroes( *armies[k_side_attacker] );
			break;

		case k_result_both_lost:
			kill_heroes( *armies[k_side_attacker] );
			kill_heroes( *armies[k_side_defender] );
			break;
	}

	// Resurrect heroes who end up "in" a town. To make campaign heroes a little less fragile,
	// this includes retreating or surrendering heroes, and heroes who just took over
	// a town or defended a town

	bool resurrect[k_side_count];

	std::string resurrect_message[k_side_count];


	t_combat_side side;

	for ( side = t_combat_side( 0 ); side < k_side_count; enum_incr( side ) )
	{
		resurrect[ side ] = false;
	}

	if ( town )
	{
		if ( attacker_won( result ) )
		{
			resurrect[k_side_attacker] = true;
			resurrect_message[k_side_attacker] = k_text_resurrect_heroes;
		}
		else
		{
			resurrect[k_side_defender] = true;
			resurrect_message[k_side_defender] = k_text_resurrect_heroes;
		}
	}

	switch (result)
	{
		case k_result_attacker_retreated:
		case k_result_attacker_surrendered:
		case k_result_attacker_escaped:
			{
				t_town const*           destination_town;
				t_adv_map_point		    destination_location;

				destination_town = find_nearest_town( map, players[k_side_attacker], location, destination_location, 
													  armies[k_side_attacker], town, 0, 0, true );
				resurrect[k_side_attacker] = true;
				resurrect_message[k_side_attacker] = replace_keywords( k_text_resurrect_heroes_post_retreat, 
																       "%town", destination_town->get_name() );
			}
			break;

		case k_result_defender_retreated:
		case k_result_defender_surrendered:
		case k_result_defender_escaped:
			{
				t_town const*           destination_town;
				t_adv_map_point		    destination_location;

				destination_town = find_nearest_town( map, players[k_side_defender], location, destination_location, 
													  armies[k_side_defender], town, 0, 0, true );
				resurrect[k_side_defender] = true;
				resurrect_message[k_side_defender] = replace_keywords( k_text_resurrect_heroes_post_retreat, 
																	   "%town", destination_town->get_name() );
			}
			break;
	}


	for ( side = t_combat_side( 0 ); side < k_side_count; enum_incr( side ) )
	{
		if ( resurrect[side] )
		{
			resurrect_heroes( *(armies[side]), resurrect_message[side],
							  window );
			int i;

			for (i = 0; i < t_creature_array::k_size; ++i)
				if (losses[side][i].get_hero() != 0)
					losses[side].clear( i );
		}
	}
}

// -----------------------------------------------------------------------
// Post process the results of combat.  Generates some UI and adjusts
// the contents of the armies depending on various skills and states of
// the heros, spells in effect, etc.
// -----------------------------------------------------------------------
void post_process_apply_combat_surrender( t_creature_array**		armies,
											t_player**				players,
											t_creature_array*		losses,
											t_combat_result			result,
											t_adv_map_point const&	location,
											t_window*				window,
											t_town*					town,
											t_artifact_list			booty,
											bool const *			are_real_armies,
											t_army_ptr &			new_army )
{
	t_adventure_map * map = armies[ k_side_attacker ]->get_map();
	if ( !map )
		map = armies[ k_side_defender ]->get_map();

	new_army = NULL;

	// Handle the winner (if any)
	t_combat_side winner;

	if (attacker_won( result ))	
	{
		winner = k_side_attacker;
	}
	else if (defender_won( result ))
	{
		winner = k_side_defender;
	}
	else
		winner = k_side_count;

	// Another place we could put an artifact pile
	t_adv_map_point alternate_location = location;

	if ( armies[ k_side_attacker ]->get_position() != alternate_location )
	{
		alternate_location = armies[ k_side_attacker ]->get_position();
	}
	else if ( armies[ k_side_defender ]->get_position() != alternate_location )
	{
		alternate_location =  armies[ k_side_defender ]->get_position();
	}

	// Handle the loser(s)
	switch (result)
	{
		case k_result_attacker_retreated:
			retreat_army( *armies[ k_side_attacker ], players[ k_side_attacker ], location, town, new_army );
			assert( booty.empty() );
			break;

		case k_result_defender_retreated:
			retreat_army( *armies[ k_side_defender ], players[ k_side_defender ], location, town, new_army );
			assert( booty.empty() );
			break;

		case k_result_attacker_surrendered:
			surrender_army( *armies[ k_side_attacker ], players[ k_side_attacker ], location, town, true, new_army );
			assert( booty.empty() );
			break;

		case k_result_defender_surrendered:
			surrender_army( *armies[ k_side_defender ], players[ k_side_defender ], location, town, true, new_army );
			assert( booty.empty() );
			break;

		case k_result_attacker_escaped:
			surrender_army( *armies[ k_side_attacker ], players[ k_side_attacker ], location, town, false, new_army );
			assert( booty.empty() );
			break;

		case k_result_defender_escaped:
			surrender_army( *armies[ k_side_defender ], players[ k_side_defender ], location, town, false, new_army );
			assert( booty.empty() );
			break;

		case k_result_attacker_lost:
			check_for_prisoners( *armies[k_side_defender], players[ k_side_defender ], 
								 *armies[ k_side_attacker ], players[ k_side_attacker ], 
								 location, town, booty );
			break;

		case k_result_defender_lost:
			check_for_prisoners( *armies[k_side_attacker], players[ k_side_attacker ], 
								 *armies[ k_side_defender ], players[ k_side_defender ], 
								 location, town, booty );
			break;

		case k_result_both_lost:
			grab_artifacts( armies, booty );
			break;

		default:
			assert( false );
			return;
	}

	t_creature_array * victor;
	t_player		 * victorious_player;
	t_creature_array * loser;
	t_player		 * defeated_player;
	bool			   victor_is_real_army;

	if ( winner == k_side_count )
	{
		victor = NULL;
		victorious_player = players[ k_side_attacker ]; // Still need to pass the two players in
		loser = NULL;
		defeated_player = players[ k_side_defender ];
		victor_is_real_army = false;
	}
	else
	{
		victor = armies[ winner ];
		victorious_player = players[ winner ];
		loser = armies[ other_side( winner ) ];
		defeated_player = players[ other_side( winner ) ];
		victor_is_real_army = are_real_armies[ winner ];
	}

	distribute_booty( window,  
					  victor, victorious_player, 
					  loser, defeated_player, 
					  victor_is_real_army, 
					  location, alternate_location,
					  booty, map );
}

void post_process_apply_combat_results( t_creature_array**		armies,
										t_player**				players,
										t_creature_array const*	losses,
										t_combat_result			result,
										t_adv_map_point const&	location,
										t_window*				window,
										t_town*					town )
{
	t_adventure_map * map = armies[ k_side_attacker ]->get_map();
	if ( !map )
		map = armies[ k_side_defender ]->get_map();

	// The scripting system requires we hold onto defeated neutral heroes...  It is possible to make reference
	// to them at a later point.
	for (int i = 0; i < t_creature_array::k_size; i++)
	{
		t_hero* hero = NULL;
		if ((players[k_side_defender] == NULL) && defender_lost(result))
		{
			hero = (*armies[k_side_defender])[i].get_hero();
			if (hero) 
				map->add_defeated_hero( hero );
		}
		if ((players[k_side_attacker] == NULL) && attacker_lost(result))
		{
			hero = (*armies[k_side_attacker])[i].get_hero();
			if (hero) 
				map->add_defeated_hero( hero );
		}
	}

	if (attacker_won( result ))	
	{
		post_combat_victor( *armies[k_side_attacker], players[k_side_attacker],
							losses[k_side_attacker], losses[k_side_defender], town, window );
	}
	else if (defender_won( result ))
	{
		post_combat_victor( *armies[k_side_defender], players[k_side_defender],
							losses[k_side_defender], losses[k_side_attacker], town, window );
	}
}
